﻿using System;
using System.Text;
using System.IO;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;

namespace Sharp.UnusedReferences
{
	//
	// TypeView.cs
	// finds and displays information about a type.
	//
	// Thu, 15 May 2003  10:05
	//
	//

	// see sample source at
	//  c:/Program Files/Microsoft.NET/FrameworkSDK/Samples/QuickStart/aspplus/samples/classbrowser

	public class myComparer : System.Collections.IComparer
	{
		// this is a smart comparer - used in sorting properties,
		// methods, and fields by name.  It works with MethodInfo,
		// PropertyInfo, and FieldInfo, as required, depending on
		// the type of the pass-in object.
		public int Compare(Object x, Object y)
		{
			Type t;
			t = typeof(MethodInfo);
			if(t.IsInstanceOfType(x))
			{
				MethodInfo i1 = (MethodInfo)x;
				MethodInfo i2 = (MethodInfo)y;
				return i1.Name.CompareTo(i2.Name);
			}

			t = typeof(PropertyInfo);
			if(t.IsInstanceOfType(x))
			{
				PropertyInfo i1 = (PropertyInfo)x;
				PropertyInfo i2 = (PropertyInfo)y;
				return i1.Name.CompareTo(i2.Name);
			}
			t = typeof(FieldInfo);
			if(t.IsInstanceOfType(x))
			{
				FieldInfo i1 = (FieldInfo)x;
				FieldInfo i2 = (FieldInfo)y;
				return i1.Name.CompareTo(i2.Name);
			}

			return 0;
		}
	}


	public class TypeView
	{
		public Boolean verbose = false;
		private Type mt = null;
		private System.Type TryLoad(String theTypeName, String theAssemblyString)
		{
			System.Type t = null;
			// load from a name like System.Data
			System.Reflection.Assembly a = System.Reflection.Assembly.Load(theAssemblyString);
			if(a == null)
				a = System.Reflection.Assembly.LoadFrom(theAssemblyString);  // load from a physical filename 

			if(a != null)
				t = a.GetType(theTypeName, false, true);

			return t;  // maybe null
		}

		private void AppendParams(System.Text.StringBuilder sb, System.Reflection.ParameterInfo[] pi, bool isProperty)
		{
			int j;
			String openBracket = "(";
			String closeBracket = ")";
			if(isProperty)
			{
				openBracket = "[";
				closeBracket = "]";
			}
			for(j = 0; j < pi.Length; j++)
			{
				if(j == 0) sb.Append(openBracket);
				else sb.Append(", ");
				ParameterAttributes Attributes = pi[j].Attributes;
				if(Attributes != ParameterAttributes.None) sb.Append(Attributes.ToString());
				sb.Append(pi[j].ParameterType.ToString()).Append(" ").Append(pi[j].Name);
			}
			if(isProperty)
			{
				if(j != 0) sb.Append(closeBracket);
			}
			else
			{
				if(j == 0) sb.Append(openBracket);
				sb.Append(closeBracket);
			}
			return;
		}
		private void EmitConstructors()
		{
			System.Reflection.ConstructorInfo[] ci = mt.GetConstructors();
			Console.WriteLine("Constructors:\n");
			if((ci == null) || (ci.Length == 0))
				Console.WriteLine("  -none-\n");
			else
			{
				System.Text.StringBuilder sb1 = new System.Text.StringBuilder();
				foreach(ConstructorInfo c in ci)
				{
					try
					{
						sb1.Append("  ").Append(mt.Name);  // c.Name == .ctor
						AppendParams(sb1, c.GetParameters(), false);
						sb1.Append("\n");
					}
					catch(Exception e)
					{
						sb1.Append("Exception: " + e.Message);
						continue;
					}
				}
				Console.WriteLine(sb1.ToString());
			}

			return;
		}
		private String EmitMethodAttrs(MethodInfo m)
		{
			System.Text.StringBuilder result = new System.Text.StringBuilder();

			if(m.IsPublic)
				result.Append("public ");

			if(m.IsFamily)
				result.Append("protected ");

			if(m.IsPrivate)
				result.Append("private ");

			if(m.IsAbstract)
				result.Append("abstract ");

			if(m.IsStatic)
				result.Append("static ");

			if(m.IsFinal)
				result.Append("final ");

			return result.ToString();
		}
		private void EmitMethods()
		{
			System.Reflection.MethodInfo[] mi = mt.GetMethods();
			Console.WriteLine("Methods:\n");
			if((mi == null) || (mi.Length == 0))
				Console.WriteLine("  -none-\n");
			else
			{
				System.Array.Sort(mi, new myComparer());

				System.Text.StringBuilder sb1 = new System.Text.StringBuilder();

				foreach(MethodInfo m in mi)
				{
					try
					{
						if(!m.IsSpecialName)
						{
							sb1.Append("  ").Append(EmitMethodAttrs(m));
							sb1.Append("  ").Append(m.ReturnType.ToString());
							sb1.Append("  ").Append(m.Name);
							AppendParams(sb1, m.GetParameters(), false);
							sb1.Append("\n");
						}
					}
					catch(Exception e)
					{
						sb1.Append("Exception: " + e);
						continue;
					}

				}
				Console.WriteLine(sb1.ToString());
			}

			return;
		}
		private string TypeAttrsString(System.Type t)
		{
			System.Text.StringBuilder sb = new System.Text.StringBuilder();
			if(t.IsPublic) sb.Append("public ");
			if(t.IsAbstract) sb.Append("abstract ");
			if(t.IsSealed) sb.Append("sealed ");
			if(t.IsEnum) sb.Append("enum ");
			if(t.IsInterface) sb.Append("interface ");
			if(t.IsClass) sb.Append("class ");
			return sb.ToString();
		}
		private String PropAttrsString(PropertyInfo p)
		{
			System.Text.StringBuilder result = new System.Text.StringBuilder();
			System.Reflection.MethodInfo mi = null;
			String access = null;

			if(p.GetGetMethod() != null)
			{
				mi = p.GetGetMethod();
				if(p.GetSetMethod() != null)
					access = "read/write ";
				else
					access = "readonly ";

			}
			else if(p.GetSetMethod() != null)
			{
				mi = p.GetSetMethod();
				access = "writeonly ";
			}

			if(mi != null)
			{
				if(mi.IsPublic)
					result.Append("public ");
				if(mi.IsPrivate)
					result.Append("private ");
				if(mi.IsFamily)
					result.Append("protected ");

				if(mi.IsStatic)
					result.Append("static ");
			}

			if(access != null)
				result.Append(access);

			return result.ToString();
		}
		private void EmitProperties()
		{
			System.Reflection.PropertyInfo[] pi = mt.GetProperties();
			Console.WriteLine("Properties:\n");
			if((pi == null) || (pi.Length == 0))
				Console.WriteLine("  -none-\n");
			else
			{
				System.Array.Sort(pi, new myComparer());  // by name
				System.Text.StringBuilder sb1 = new System.Text.StringBuilder();
				foreach(PropertyInfo p in pi)
				{
					try
					{
						sb1.Append("  ").Append(PropAttrsString(p));
						sb1.Append("  ").Append(p.PropertyType.ToString());
						sb1.Append("  ").Append(p.Name);
						AppendParams(sb1, p.GetIndexParameters(), true);
						sb1.Append("\n");
					}
					catch(Exception e)
					{
						sb1.Append("Exception: " + e.Message);
						continue;
					}
				}
				Console.WriteLine(sb1.ToString());
			}
			return;
		}
		private String FieldAttrsString(FieldInfo f)
		{
			System.Text.StringBuilder result = new System.Text.StringBuilder();
			if(f.IsPublic) result.Append("public ");
			if(f.IsFamily) result.Append("protected ");
			if(f.IsPrivate) result.Append("private ");
			if(f.IsLiteral) result.Append("const ");
			if(f.IsStatic) result.Append("static ");
			return result.ToString();
		}
		private void EmitFields()
		{
			System.Reflection.FieldInfo[] fi = mt.GetFields();
			Console.WriteLine("Fields:\n");
			if((fi == null) || (fi.Length == 0))
				Console.WriteLine("  -none-\n");
			else
			{
				System.Array.Sort(fi, new myComparer());  // by name
				System.Text.StringBuilder sb1 = new System.Text.StringBuilder();

				foreach(FieldInfo f in fi)
				{
					try
					{
						sb1.Append("  ").Append(FieldAttrsString(f));
						sb1.Append("  ").Append(f.FieldType.ToString());
						sb1.Append("  ").Append(f.Name).Append("\n");
					}
					catch(Exception e)
					{
						sb1.Append("Exception: " + e.Message);
						continue;
					}
				}
				Console.WriteLine(sb1.ToString());

			}
			return;
		}
		private System.Collections.Stack classHierarchy;
		private void BuildClassHierarchy(Type t)
		{
			classHierarchy.Push(t);
			if(t.BaseType != null)
				BuildClassHierarchy(t.BaseType);
		}
		private Type ReferencedType(Type t)
		{
			return (t.HasElementType) ? ReferencedType(t.GetElementType()) : t;
		}
		private String TypeStringWithInterfaces(Type t1)
		{

			StringBuilder result = new StringBuilder(t1.ToString());

			Type t = ReferencedType(t1);

			Type[] it = t.GetInterfaces();
			if(it.Length > 0)
			{
				int j = 0;
				for(int i = 0; i < it.Length; i++)
				{
					if((t.BaseType != null) && !(it[i].IsAssignableFrom(t.BaseType)))
					{
						if(j == 0) result.Append(" : ");
						else result.Append(", ");
						result.Append(it[i].ToString());
						j++;
					}
				}
			}

			return result.ToString();
		}

		public void EmitHierarchy()
		{
			if(mt == null) return;
			classHierarchy = new System.Collections.Stack();
			BuildClassHierarchy(mt);
			Console.WriteLine("Hierarchy:\n");
			System.Text.StringBuilder sb = new System.Text.StringBuilder();
			int i = 0;
			while(classHierarchy.Count != 0)
			{
				for(int j = 0; j < i + 1; j++)
				{
					sb.Append("  ");
				}
				if(i != 0) sb.Append("+ ");
				sb.Append(TypeStringWithInterfaces((Type)classHierarchy.Pop())).Append("\n");
				i++;
			}

			Console.WriteLine(sb.ToString());
			return;
		}
		private void DoOneType(Type t)
		{
			mt = t;
			Console.WriteLine("{0} {1}\n\nAssembly: {2}\n", TypeAttrsString(mt), mt.ToString(), mt.AssemblyQualifiedName);

			EmitHierarchy();
			EmitConstructors();
			EmitMethods();
			EmitProperties();
			EmitFields();
			//EmitEvents(); 
		}
		private void Run(String[] args)
		{

			string theTypeName;

			if(args.Length == 1)
			{
				if(System.IO.File.Exists(args[0]))
				{  // (args[0].Endswith(".dll"))
					// display all types in a given dll
					System.Reflection.Assembly a = System.Reflection.Assembly.LoadFrom(args[0]);
					Type[] tset = a.GetTypes();
					System.Console.WriteLine("Types contained in {0}:", args[0]);
					foreach(Type t1 in tset)
					{
						System.Console.WriteLine("  " + t1.ToString());
					}
					System.Console.WriteLine("\n--------------------------------------------\n");
					foreach(Type t1 in tset)
					{
						DoOneType(t1);
					}
				}
				else
				{
					// file with name arg[0] does not exist, so assume it is a typename
					theTypeName = args[0];
					// apply a heuristic here.  Try loading...Guess the assembly name: 
					//  (1) mscorlib
					//  (2) System
					//  (3) an assembly name derived from a fully qualified typename
					// then fail.

					Type t = TryLoad(theTypeName, "mscorlib");

					if(t == null)
						t = TryLoad(theTypeName, "System");

					if(t == null)
					{
						int p = theTypeName.LastIndexOf('.');
						t = TryLoad(theTypeName, theTypeName.Substring(0, p));
					}

					if(t != null)
						DoOneType(t);
					else
						System.Console.WriteLine("Cannot load a type by the name '{0}'", theTypeName);

				}
			}
			else
			{
				// more than one argument
				theTypeName = args[0];
				string theAssemblyName = args[1];
				Type t = TryLoad(theTypeName, theAssemblyName);
				if(t != null)
					DoOneType(t);
				else
					System.Console.WriteLine("Cannot load a type by the name '{0}'\n from assembly '{1}'",
								 theTypeName, theAssemblyName);
			}

			return;
		}
		private static void DisplayUsage()
		{
			Console.WriteLine("usage::\n   TypeView.exe  <classname>  [-a <assemblyName>]");
		}
		public static int Main(string[] args)
		{

			if(args.Length < 1)
			{
				DisplayUsage();
				return 0;
			}

			TypeView c = new TypeView();
			c.Run(args);
			return 0;

		}
	}
}
